/* 
 * File:   photonMap.h
 * Author: flo
 *
 * Created on July 9, 2010, 1:47 PM
 */

#ifndef _PHOTONMAP_H
#define	_PHOTONMAP_H

#include "rtStructs.h"
#include "render.h"

//#include <iostream>
//#include <math.h>

#include <algorithm>
#include <functional>
#include <vector>

using namespace std;

#define GLOBAL_PMAP_MAX_SIZE 87477
#define CAUSTIC_PMAP_MAX_SIZE 8376

struct PMPhoton {
    Vec3 position;
    Vec3 inDir;
    Vec3 energy;
    int axis;
};

class photonMap : public Render {
private:
    // Define a template class vector of PMPhoton
    typedef vector<PMPhoton> PMap;
    //Define an iterator for template class vector of PMPhoton
    typedef PMap::iterator PMapIt;
    typedef PMap::const_iterator PMapCIt;

    PMap m_vGlobalPMap;
    PMap m_vCausticPMap;

public:
    // Heap for getting nearest photons

    class PMHeap {
    public:
        float max_sqr_dist;
        int size;
        Vec3 refPos;
        PMap nearestPhotons;
        PMap* pOrgPMap;
    private:

        class Compare {
            Vec3 refP;
        public:

            Compare(Vec3 reference) {
                refP = reference;
            }
            // Compare two Foo structs.

            bool operator() (const PMPhoton& x, const PMPhoton& y) const {
                return (y.position - refP).length_sqr() < (y.position - refP).length_sqr();
            }
        };
        Compare compObj;

    public:

        void add(const PMPhoton& photon) {
            float new_dist = (photon.position - refPos).length_sqr();
            if (new_dist < max_sqr_dist) {
                pop_heap(nearestPhotons.begin(), nearestPhotons.end(), compObj);
                nearestPhotons.pop_back();
                nearestPhotons.push_back(photon);
                push_heap(nearestPhotons.begin(), nearestPhotons.end(), compObj);
                max_sqr_dist = new_dist;
            }
        }

        PMHeap(PMap* pPMap, Vec3 reference, int num_photons) : compObj(reference) {
            pOrgPMap = pPMap;
            refPos = reference;
            max_sqr_dist = FLT_MAX;
            size = 0;
            nearestPhotons.reserve(num_photons);
            while (size < num_photons) {
                //push some onto the heap
                nearestPhotons.push_back((*pOrgPMap)[size]);
                size++;
            }
            make_heap(nearestPhotons.begin(), nearestPhotons.end(), compObj);
            max_sqr_dist = (nearestPhotons.front().position - refPos).length_sqr();
        }
    };

private:
    bool init();
    void genPhoton();

public:
    photonMap(Scene* scene);
    virtual ~photonMap();

    // going through the whole array of Photons
    PMHeap getNearestPhotons_linear(PMap* pmap, Vec3 reference, int num_photons);
    //PMHeap getNearestPhotons_kd(Vec3 reference, int num_photons);
    //PMHeap reGetNearestPhotons_kd(PMHeap);
};


#endif	/* _PHOTONMAP_H */

